Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.buildbetter.ai/llms.txt

Use this file to discover all available pages before exploring further.

Quick Start

1

Get your credentials

Find your API key in Settings > API Keys and your Feedback Source ID on your feedback source’s detail page.
2

Add the script tag

Paste this snippet into your HTML, replacing the placeholder values:
<script
  src="https://app.buildbetter.app/widget/feedback-widget.js"
  data-api-key="YOUR_API_KEY"
  data-feedback-source-id="YOUR_SOURCE_ID"
></script>
3

You're live

A floating “Feedback” button appears in the bottom-right corner with a default text field. Users can click it to submit feedback directly to your BuildBetter feedback source.

Data Attributes

When using the simple script tag, customize basic settings with data-* attributes:
AttributeDefaultDescription
data-api-key(required)Your organization API key
data-feedback-source-id(required)The feedback source to submit to
data-mode"floating"Display mode: floating, popover, inline, or slide-out
data-position"bottom-right"Button position (floating mode): bottom-right, bottom-left, top-right, top-left
data-button-text"Feedback"Text on the trigger button
data-title"Send Feedback"Title at the top of the form

Programmatic API

For full control, use the JavaScript API. The script exposes a global BuildBetterFeedback object.
<script src="https://app.buildbetter.app/widget/feedback-widget.js"></script>
<script>
  const widget = BuildBetterFeedback.create({
    apiKey: "YOUR_API_KEY",
    feedbackSourceId: "YOUR_SOURCE_ID",
    mode: "floating",
    position: "bottom-right",
    buttonText: "Send Feedback",
    title: "How can we improve?",
    fields: [
      { type: "emoji", name: "sentiment", label: "How do you feel?", required: true },
      { type: "textarea", name: "comments", label: "Tell us more", placeholder: "Your thoughts..." },
    ],
  });
</script>

Instance Methods

BuildBetterFeedback.create() returns a widget instance with the following methods:
MethodDescription
widget.open()Open the widget (floating, popover, and slide-out modes only)
widget.close()Close the widget
widget.destroy()Remove the widget from the DOM entirely

Multiple Instances

You can create multiple widget instances on the same page. Each instance is fully isolated with its own Shadow DOM — styles never leak between widgets or affect your page.
// Quick reaction widget
BuildBetterFeedback.create({
  apiKey: "YOUR_API_KEY",
  feedbackSourceId: "SOURCE_1",
  mode: "floating",
  position: "bottom-right",
  fields: [
    { type: "emoji", name: "reaction", label: "Quick feedback", required: true },
  ],
});

// Detailed feedback form (inline)
BuildBetterFeedback.create({
  apiKey: "YOUR_API_KEY",
  feedbackSourceId: "SOURCE_2",
  mode: "inline",
  container: "#feedback-form",
  fields: [
    { type: "text", name: "title", label: "Subject", required: true },
    { type: "textarea", name: "details", label: "Description", required: true },
    { type: "select", name: "category", label: "Category", options: ["Bug", "Feature Request", "Question", "Other"] },
  ],
});

Configuration Reference

apiKey
string
required
Your organization API key (bb_org_...). Found in Settings > API Keys.
feedbackSourceId
string
required
The ID of the feedback source to submit records to.
mode
string
default:"floating"
Display mode. One of floating, popover, inline, or slide-out. See Display Modes.
position
string
default:"bottom-right"
Trigger button position (floating mode only). One of bottom-right, bottom-left, top-right, top-left.
openDirection
string
default:"above"
Whether the form opens above or below the button (popover mode only). One of above or below.
container
string | HTMLElement
CSS selector or DOM element reference for inline and popover modes. Required when mode is inline. Optional for popover — if provided, the button renders inside the container; otherwise it appends to the body.
buttonText
string
default:"Feedback"
Text displayed on the trigger button (floating, popover, and slide-out modes).
title
string
default:"Send Feedback"
Title displayed at the top of the feedback form.
fields
FieldConfig[]
Array of field configurations. See Field Types. Defaults to a single required textarea.
theme
ThemeConfig
Visual customization options. See Theming.
person
PersonConfig
Pre-fill user identity as hidden context. See User Identity.
metadata
Record<string, string>
Hidden key-value pairs included with every submission. See Metadata.
hideBranding
boolean
default:"false"
Remove the “Powered by BuildBetter” footer from the widget.
onSubmit
(result: SubmissionResult) => void
Callback fired after a successful submission. Receives an object with id and fields.
onError
(error: Error) => void
Callback fired when a submission fails.
onOpen
() => void
Callback fired when the widget opens.
onClose
() => void
Callback fired when the widget closes.

Display Modes

A floating button fixed to a corner of the viewport. Clicking it opens a feedback card near the button.
BuildBetterFeedback.create({
  apiKey: "YOUR_API_KEY",
  feedbackSourceId: "YOUR_SOURCE_ID",
  mode: "floating",
  position: "bottom-right", // or "bottom-left", "top-right", "top-left"
  buttonText: "Feedback",
});

Field Types

Configure which fields appear in your feedback form using the fields array. Each field requires a type, name (used as the key when submitting), and label (displayed to the user).
A single-line text input.
{
  type: "text",
  name: "title",
  label: "Title",
  required: true,
  placeholder: "Enter a title..."
}
A multi-line text area for longer responses.
{
  type: "textarea",
  name: "details",
  label: "Details",
  placeholder: "Tell us more..."
}
A row of emoji buttons for quick sentiment feedback. The text label (not the emoji character) is submitted as the value, making it easy to filter and analyze.
{
  type: "emoji",
  name: "sentiment",
  label: "How was your experience?",
  required: true
}
Default emojis: angry, sad, neutral, happy, loveYou can customize the emojis and their labels using emojiOptions:
{
  type: "emoji",
  name: "sentiment",
  label: "Rate this feature",
  required: true,
  emojiOptions: [
    { emoji: "👎", label: "Not useful" },
    { emoji: "😐", label: "Okay" },
    { emoji: "👍", label: "Useful" },
    { emoji: "🔥", label: "Love it" },
  ]
}
A 1-5 star selector. Submits the numeric value as a string (e.g. "4").
{
  type: "star-rating",
  name: "rating",
  label: "Rate your experience",
  required: true
}
A dropdown with predefined options.
{
  type: "select",
  name: "category",
  label: "Category",
  options: ["Bug Report", "Feature Request", "General Feedback", "Other"]
}
A 0-10 horizontal scale. Submits the numeric value as a string (e.g. "8").
{
  type: "nps",
  name: "score",
  label: "How likely are you to recommend us?",
  required: true
}

Field Properties

PropertyTypeRequiredDescription
typestringYesOne of: text, textarea, emoji, star-rating, select, nps
namestringYesUnique key for this field (used in the submitted data)
labelstringYesLabel displayed above the field
requiredbooleanNoIf true, the field must have a value before submitting
placeholderstringNoPlaceholder text (text and textarea only)
optionsstring[]NoDropdown options (select only)
emojiOptionsEmojiOption[]NoCustom emoji choices (emoji only). Each item has emoji (the character) and label (the submitted value).
categorystringNoOverride the API field category (advanced)

Theming

Customize the widget’s appearance to match your brand. The theme applies to all display modes including the inline form background.
BuildBetterFeedback.create({
  apiKey: "YOUR_API_KEY",
  feedbackSourceId: "YOUR_SOURCE_ID",
  theme: {
    primaryColor: "#6366f1",
    backgroundColor: "#ffffff",
    textColor: "#1f2937",
    borderRadius: 8,
    fontFamily: "Inter, sans-serif",
  },
});
PropertyTypeDefaultDescription
primaryColorstring#6366f1Accent color for buttons, selections, and highlights
backgroundColorstring#ffffffBackground color of the form card
textColorstring#1f2937Color of labels and body text
borderRadiusnumber8Corner radius in pixels
fontFamilystringSystem font stackCSS font-family value
The widget uses theme-relative borders (derived from textColor) so input fields and controls remain visible on both light and dark backgrounds.

User Identity

If your users are already logged in to your application, you can pass their identity to the widget so feedback is automatically linked to the person in BuildBetter — no need to ask them to fill in their name or email.
BuildBetterFeedback.create({
  apiKey: "YOUR_API_KEY",
  feedbackSourceId: "YOUR_SOURCE_ID",
  person: {
    email: currentUser.email,
    firstName: currentUser.firstName,
    lastName: currentUser.lastName,
  },
  fields: [
    { type: "emoji", name: "reaction", label: "How was this?", required: true },
    { type: "textarea", name: "feedback", label: "Comments" },
  ],
});
The person data is sent as hidden context — it is never displayed in the form. When an email is provided, the widget upserts a person record in BuildBetter and links the feedback submission to them. If the person already exists (matched by email), their record is reused. Name fields are only updated when provided — omitting them won’t overwrite existing data.
PropertyTypeDescription
emailstringEmail address (used to match or create the person)
firstNamestringFirst name
lastNamestringLast name

Metadata

Pass hidden key-value data with every submission. Metadata fields are never shown in the form — they’re submitted automatically alongside the user’s responses and appear as additional columns in your feedback table. This is useful for capturing page context, app state, or any other information that helps you understand where and when the feedback was given.
BuildBetterFeedback.create({
  apiKey: "YOUR_API_KEY",
  feedbackSourceId: "YOUR_SOURCE_ID",
  metadata: {
    page_url: window.location.href,
    app_version: "2.1.0",
    plan: "pro",
    doc_section: "getting-started",
  },
  fields: [
    { type: "emoji", name: "helpful", label: "Was this page helpful?", required: true },
    { type: "textarea", name: "feedback", label: "Any other thoughts?" },
  ],
});
Each key becomes a column in the feedback records table (e.g. page_url appears as “Page Url”). All metadata values are stored as strings with category: "metadata".
person vs metadata: Use person for user identity (email, name) — it creates/links a person record in BuildBetter. Use metadata for contextual data about the submission (page URL, plan, app version) — it’s stored as fields on the feedback record itself.

Event Callbacks

React to widget lifecycle events with callback functions.
const widget = BuildBetterFeedback.create({
  apiKey: "YOUR_API_KEY",
  feedbackSourceId: "YOUR_SOURCE_ID",

  onSubmit: (result) => {
    console.log("Feedback submitted:", result.id);
    // result.fields contains the submitted field values
  },

  onError: (error) => {
    console.error("Submission failed:", error.message);
  },

  onOpen: () => {
    console.log("Widget opened");
  },

  onClose: () => {
    console.log("Widget closed");
  },
});

Examples

A floating NPS survey with optional follow-up, linked to the logged-in user.
BuildBetterFeedback.create({
  apiKey: "YOUR_API_KEY",
  feedbackSourceId: "YOUR_SOURCE_ID",
  mode: "floating",
  position: "bottom-right",
  title: "How are we doing?",
  fields: [
    { type: "nps", name: "nps_score", label: "How likely are you to recommend us?", required: true },
    { type: "textarea", name: "reason", label: "What's the main reason for your score?", placeholder: "Optional" },
  ],
  person: {
    email: currentUser.email,
    firstName: currentUser.name,
  },
  theme: {
    primaryColor: "#2563eb",
  },
});
A popover button embedded in your documentation pages that captures the current URL automatically.
<div id="docs-feedback"></div>
<script src="https://app.buildbetter.app/widget/feedback-widget.js"></script>
<script>
  BuildBetterFeedback.create({
    apiKey: "YOUR_API_KEY",
    feedbackSourceId: "YOUR_SOURCE_ID",
    mode: "popover",
    container: "#docs-feedback",
    openDirection: "above",
    buttonText: "Was this helpful?",
    title: "Page Feedback",
    fields: [
      { type: "emoji", name: "helpful", label: "How helpful was this page?", required: true },
      { type: "textarea", name: "feedback", label: "How can we improve it?", placeholder: "Optional" },
    ],
    metadata: {
      page_url: window.location.href,
      page_title: document.title,
    },
  });
</script>
An inline form embedded in your page for collecting detailed feature requests.
<h2>Request a Feature</h2>
<div id="feature-request"></div>

<script src="https://app.buildbetter.app/widget/feedback-widget.js"></script>
<script>
  BuildBetterFeedback.create({
    apiKey: "YOUR_API_KEY",
    feedbackSourceId: "YOUR_SOURCE_ID",
    mode: "inline",
    container: "#feature-request",
    title: "Tell us what you need",
    fields: [
      { type: "text", name: "title", label: "Feature title", required: true, placeholder: "Brief description" },
      { type: "textarea", name: "description", label: "Details", required: true, placeholder: "Describe the feature and why it would be useful..." },
      { type: "select", name: "priority", label: "How important is this?", options: ["Nice to have", "Important", "Critical"] },
      { type: "select", name: "area", label: "Product area", options: ["Dashboard", "Reports", "Integrations", "API", "Other"] },
    ],
    person: {
      email: currentUser.email,
      firstName: currentUser.firstName,
      lastName: currentUser.lastName,
    },
  });
</script>
A minimal floating widget with custom emojis for page-level feedback.
BuildBetterFeedback.create({
  apiKey: "YOUR_API_KEY",
  feedbackSourceId: "YOUR_SOURCE_ID",
  mode: "floating",
  position: "bottom-left",
  buttonText: "Rate this page",
  title: "Quick Feedback",
  fields: [
    {
      type: "emoji",
      name: "reaction",
      label: "Was this page helpful?",
      required: true,
      emojiOptions: [
        { emoji: "👎", label: "Not helpful" },
        { emoji: "😐", label: "Somewhat helpful" },
        { emoji: "👍", label: "Very helpful" },
      ],
    },
  ],
  theme: {
    primaryColor: "#10b981",
    borderRadius: 12,
  },
});
A slide-out panel for structured bug reports with severity rating.
BuildBetterFeedback.create({
  apiKey: "YOUR_API_KEY",
  feedbackSourceId: "YOUR_SOURCE_ID",
  mode: "slide-out",
  title: "Report an Issue",
  fields: [
    { type: "star-rating", name: "severity", label: "How severe is this issue?", required: true },
    { type: "text", name: "summary", label: "Summary", required: true, placeholder: "Brief description of the issue" },
    { type: "textarea", name: "steps", label: "Steps to reproduce", placeholder: "1. Go to...\n2. Click on...\n3. See error" },
    { type: "select", name: "browser", label: "Browser", options: ["Chrome", "Firefox", "Safari", "Edge", "Other"] },
  ],
  metadata: {
    page_url: window.location.href,
    user_agent: navigator.userAgent,
  },
});
A fully dark-themed floating widget. The widget uses theme-relative borders so controls stay visible on dark backgrounds.
BuildBetterFeedback.create({
  apiKey: "YOUR_API_KEY",
  feedbackSourceId: "YOUR_SOURCE_ID",
  mode: "floating",
  position: "bottom-right",
  title: "Feedback",
  fields: [
    { type: "emoji", name: "sentiment", label: "How do you feel?", required: true },
    { type: "textarea", name: "feedback", label: "Tell us more" },
  ],
  theme: {
    primaryColor: "#22c55e",
    backgroundColor: "#1a1a2e",
    textColor: "#e2e8f0",
    borderRadius: 12,
  },
});

Widget Builder

You can build and preview your widget visually inside BuildBetter. Go to Feedback > Widget Builder to configure fields, display mode, and theme with a live interactive preview that runs the real widget, then copy the generated embed code.

Technical Details

Lightweight

~12 KB gzipped. Single JS file, no CSS dependency, no external libraries needed on your page.

Style Isolation

Each widget instance renders inside a Shadow DOM, so its styles never conflict with your page.

Spam Prevention

Built-in 5-second cooldown between submissions. Each submission includes a unique ID for server-side deduplication.

Browser Support

Works in all modern browsers: Chrome, Firefox, Safari, and Edge. See Browser Compatibility for details.

Browser Compatibility

The widget uses Shadow DOM for style isolation, which requires a modern browser. Internet Explorer is not supported.
BrowserMinimum VersionReleased
Chrome53+2016
Edge79+2020
Firefox63+2018
Safari10+2016
Safari iOS10+2016
Chrome Android53+2016
Internet Explorer 11 and legacy Edge (pre-Chromium) are not supported. The widget will not render in these browsers.